package manager.search.service.impl;

import common.PageView;
import common.Pager;
import common.ResponseEntity;
import manager.brand.dao.BrandDao;
import manager.brand.vo.BrandVO;
import manager.categories.dao.CategoryDao;
import manager.categories.vo.CategoryVO;
import manager.goods.dao.SpuDao;
import manager.goods.service.GoodsService;
import manager.goods.vo.SpuVO;
import manager.search.repository.GoodsRepository;
import manager.search.service.SearchService;
import manager.search.vo.SpuConditionEsVO;
import manager.search.vo.SpuEsVO;
import manager.spec.params.dao.ParamDao;
import manager.spec.params.vo.SpecParamVO;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.util.Sqls;

import java.util.*;
import java.util.stream.Collectors;
@Service(value = "searchService")
public class SearchServiceImpl implements SearchService {
    @Autowired
    private GoodsService goodsService;
    @Autowired
    private GoodsRepository goodsRepository;
    @Autowired
    private SpuDao spuDao;
    @Autowired
    private ParamDao paramDao;
    @Autowired
    private CategoryDao categoryDao;
    @Autowired
    private BrandDao brandDao;
    /**
     * 分页查询商品数据
     * @param spuConditionEsVO
     * @return
     */
    @Override
    public ResponseEntity<PageView<List<SpuEsVO>>> getGoodsByPageCondition(SpuConditionEsVO spuConditionEsVO) {
            ResponseEntity<PageView<List<SpuEsVO>>> responseEntity=new ResponseEntity<>();
            //基本查询条件
            NativeSearchQueryBuilder nativeSearchQueryBuilder=new NativeSearchQueryBuilder();
            //构建分页查询条件
            SearchQuery query=getQueryCondition(spuConditionEsVO,nativeSearchQueryBuilder);
            Object resultObject=goodsRepository.search(query);
            //获取聚合值
            Map<String,Object> paramsResult=getParamsByResult(resultObject);
            //获取分页数据
            PageView<List<SpuEsVO>> pageVO=getPageResult(resultObject);
            responseEntity.setStatusCode(0);
            responseEntity.setBody(pageVO);
            //获取分类信息
            Map<String,Object> categoryInfo=new LinkedHashMap<>();
            getCategoryInfos(spuConditionEsVO.getCategoryId(),categoryInfo);
            paramsResult.put("categoryInfo",categoryInfo);
            responseEntity.setExtResult(paramsResult);
            return responseEntity;
    }
    /**
     * 根据分类id获取三级分类名称
     */
    private Map<String,Object> getCategoryInfos(String categoryId,Map<String,Object> resultMap){
            Example example = new Example(CategoryVO.class);
            Example.Criteria criteria = example.createCriteria();
            criteria.andEqualTo("id",categoryId);
            List<CategoryVO> searchSpecParams=categoryDao.selectByExample(example);
            if(searchSpecParams.size()>0){
                resultMap.put(searchSpecParams.get(0).getId(),searchSpecParams.get(0).getName());
                getCategoryInfos(searchSpecParams.get(0).getParentId(), resultMap);
            }
            return resultMap;
    }

    /**
     * 获取分页数据结果
     * @param resultObject
     * @return
     */
    private PageView<List<SpuEsVO>> getPageResult(Object resultObject) {
            PageView<List<SpuEsVO>> pageVO=new PageView<>();
            Page<SpuEsVO> spusPage=(Page<SpuEsVO>)resultObject;
            pageVO.setRows(spusPage.getContent());
            //补全分类和品牌
            getBrandAndCategory(spusPage.getContent());
            pageVO.setTotal((int)spusPage.getTotalElements());
            return pageVO;
    }
    /**
     * 补全分类和品牌
     */
     private Map<String, Object>  getBrandAndCategory(List<SpuEsVO> spuEsVOS){
           Map<String, Object> result=new HashMap<>();
           //获取分类后去重的分类id
           List<String> categoryIds = spuEsVOS.stream().map(SpuEsVO::getCategoryId).collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
           List<String> brands = spuEsVOS.stream().map(SpuEsVO::getBrandId).collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
           //分别查询分类和品牌并且从数据库中获取名称,补充的数据列表里面去
           Map<String,Object> categoryInfo=getCategorysByIds(categoryIds);
           List<BrandVO> brandVOList=new ArrayList<>();
           if(brands.size()>0&&null!=brands.get(0)){
               brandVOList=getbrandsByIds(brands);
           }
           addCategoryAndBrandInfo(spuEsVOS,categoryInfo,brandVOList);
           return result;
     }

     private void addCategoryAndBrandInfo(List<SpuEsVO> spuEsVOS, Map<String, Object> categoryInfo, List<BrandVO> brandVOList) {
         spuEsVOS.forEach(item->{
                List<BrandVO>  brandVOS=brandVOList.stream().filter(k->k.getId().equals(item.getBrandId())).collect(Collectors.toList());
                if(brandVOS.size()>0){
                    item.setBrandName(brandVOS.get(0).getName());
                };
                Map<String,Object> categoryMap=(Map<String,Object>)categoryInfo.get(item.getCategoryId());
                item.setCategoryInfo(categoryMap);
         });
     }

    /**
     * 获取三级分类
     * @param categoryIds
     * @return
     */
     private Map<String,Object> getCategorysByIds(List<String> categoryIds){
          Map<String,Object> categoryVOS=new LinkedHashMap<>();
          categoryIds.forEach(categoryId->{
                Map<String,Object> resultMap=new LinkedHashMap<>();
                getCategoryInfos(categoryId,resultMap);
                categoryVOS.put(categoryId,resultMap);
          });
          return categoryVOS;
     }
    /**
     * 获取品牌
     * @param brands
     * @return
     */
     private List<BrandVO> getbrandsByIds( List<String> brands){
         Example example = new Example(BrandVO.class);
         Example.Criteria criteria = example.createCriteria();
         criteria.andIn("id",brands);
         List<BrandVO> brandList=brandDao.selectByExample(example);
         return brandList;
     }
    /**
     * 获取参数聚合结果
     * @param resultObject
     * @return
     */
    private Map<String, Object> getParamsByResult(Object resultObject) {
         Map<String, Object> result=new HashMap<>();
         AggregatedPage<SpuVO> search = (AggregatedPage)resultObject;
         if(null==search.getAggregations()){
              return result;
         }
         Map<String, Aggregation > aggregationResult=search.getAggregations().getAsMap();
         aggregationResult.keySet().forEach(k->{
             List<String> paramEnums=new ArrayList<>();
             Terms terms=(Terms)aggregationResult.get(k);
             for (Terms.Bucket bucket : terms.getBuckets()) {
                 paramEnums.add(bucket.getKey().toString());
             }
             result.put(k,paramEnums);
         });
         return result;
    }
    /**
     * 构建查询条件
     * @param spuConditionEsVO
     * @param nativeSearchQueryBuilder
     * @return
     */
    private SearchQuery getQueryCondition(SpuConditionEsVO spuConditionEsVO, NativeSearchQueryBuilder nativeSearchQueryBuilder){
        Pageable pageable= PageRequest.of(spuConditionEsVO.getPager().getCurrentPage()-1, spuConditionEsVO.getPager().getPageSize());
        BoolQueryBuilder bqb = QueryBuilders.boolQuery();
        if(StringUtils.isNotBlank(spuConditionEsVO.getCategoryId())){
            bqb.must(QueryBuilders.matchPhraseQuery("categoryId", spuConditionEsVO.getCategoryId()));
        }
        if(StringUtils.isNotBlank(spuConditionEsVO.getTitle())){
            bqb.must(QueryBuilders.fuzzyQuery("title", spuConditionEsVO.getTitle()));
        }
        if(spuConditionEsVO.getParamList()!=null&&spuConditionEsVO.getParamList().size()>0){
            spuConditionEsVO.getParamList().forEach(item->{
                String k=(String)item.get("key");
                String v=(String)item.get("value");
                bqb.must(QueryBuilders.matchPhraseQuery("specifications."+k,v));
            });
        }
        //构建聚合条件
        if(StringUtils.isNotBlank(spuConditionEsVO.getCategoryId())||StringUtils.isNotBlank(spuConditionEsVO.getTitle())){
            buildAggs(nativeSearchQueryBuilder,spuConditionEsVO);
        }
        SearchQuery query = nativeSearchQueryBuilder.withQuery(bqb).withPageable(pageable).build();
        return query;
    }

    /**
     * 构建聚合条件
     * @param nativeSearchQueryBuilder
     */
    private void buildAggs(NativeSearchQueryBuilder nativeSearchQueryBuilder,SpuEsVO spuEsVO){
           Example example=new Example(SpecParamVO.class);
           Example.Criteria criteria = example.createCriteria();
           criteria.andEqualTo("cid",spuEsVO.getCategoryId()).andEqualTo("searching",true);
           List<SpecParamVO> searchSpecParams=paramDao.selectByExample(example);
           searchSpecParams = searchSpecParams.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(SpecParamVO :: getName))), ArrayList::new));
           for (SpecParamVO specParamVO:searchSpecParams){
                String paramName=specParamVO.getName();
                TermsAggregationBuilder termsAggregationBuilder= AggregationBuilders.terms(paramName).field("specifications."+paramName+".keyword");
                nativeSearchQueryBuilder.addAggregation(termsAggregationBuilder);
          }
    }

    /**
     * 同步数据到ES中
     *
     */
    @Override
    public void addDBToES() {
            SpuVO spuVO=new SpuVO();
            Integer count=spuDao.selectByConditionCount(spuVO);
            int pageCount=count/20+1;
            for (int i = 1; i <=pageCount; i++) {
                Pager pager=new Pager();
                pager.setPageSize(20);
                spuVO.setPager(pager);
                pager.setCurrentPage(i);
                spuVO.setFromDb(true);
                PageView<List<SpuVO>> pageView=goodsService.selectByConditionPage(spuVO);
                List<SpuVO> goodsList=pageView.getRows();
                for (int j = 0; j < goodsList.size(); j++) {
                    SpuEsVO spuEsVO=getSpuEsVO(goodsList.get(j));
                    goodsRepository.save(spuEsVO);
                }
            }
    }
     @Override
     public void saveDbToEs(SpuVO spuVO){
         SpuEsVO spuEsVO=getSpuEsVO(spuVO);
         goodsRepository.save(spuEsVO);
     }
    /**
     * 删除ES中所有数据
     */
    @Override
    public void deleteDataFromES() {
         goodsRepository.deleteAll();
    }

    private SpuEsVO getSpuEsVO(SpuVO spuVO){
        SpuEsVO spuEsVO=new SpuEsVO();
        spuEsVO.setId(spuVO.getId());
        spuEsVO.setTitle(spuVO.getTitle());
        spuEsVO.setSubTitle(spuVO.getSubTitle());
        spuEsVO.setSpecifications(converParamsMapToList(spuVO.getSpuDetialVO().getSpecifications()));
        spuEsVO.setBrandId(spuVO.getBrandId());
        spuEsVO.setCategoryId(spuVO.getCategoryId());
        spuEsVO.setRecommond(spuVO.getRecommond());
        spuEsVO.setSaleable(spuVO.getSaleable());
        spuEsVO.setValid(spuVO.getValid());
        List<Map<String,Object>> resultMap=new ArrayList<>();
        spuVO.getSkus().forEach(skuItem->{
             Map<String,Object> skuItemMap=new HashMap<>();
             skuItemMap.put("id",skuItem.getId());
             skuItemMap.put("spuId",skuItem.getSpuId());
             skuItemMap.put("title",skuItem.getTitle());
             if(StringUtils.isNotBlank(skuItem.getImages())){
                 skuItemMap.put("displayImage",skuItem.getImages().split(",")[0]);
             }
             skuItemMap.put("price",skuItem.getPrice());
             skuItemMap.put("stock",skuItem.getStockVO().getStock());
             resultMap.add(skuItemMap);
        });
        spuEsVO.setSkus(resultMap);
        return spuEsVO;
    }
    //将规格参数字符串转换成VO,出入ES中
    private Map<String,Object> converParamsMapToList(String paramMapStr){
        Map<String,Object> specificationMap=new HashMap<>();
        try {
            JSONObject jsonObject = new JSONObject(paramMapStr);
            JSONArray paramsGroup=(JSONArray)jsonObject.get("groupParams");
            for (int i = 0; i < paramsGroup.length(); i++) {
                  JSONObject group=(JSONObject)paramsGroup.get(i);
                  JSONArray params=(JSONArray)group.get("params");
                  getSpecParamEsVOS(params,specificationMap);
            }
            JSONArray params=(JSONArray)jsonObject.get("params");
            getSpecParamEsVOS(params,specificationMap);
        } catch (Exception e) {
            e.printStackTrace();
        }
         return specificationMap;
    }

    private void getSpecParamEsVOS(JSONArray params,Map<String,Object> specificationMap) throws JSONException {
        for (int j = 0; j <params.length() ; j++) {
            JSONObject paramItem=(JSONObject)params.get(j);
            specificationMap.put(paramItem.optString("k"),paramItem.optString("v"));
            String options=paramItem.optString("options");
            if(options!=null&&!"".equals(options)){
                JSONArray optionsArray=new JSONArray(options);
                List<String> optionsMapList=new ArrayList<>();
                for (int k = 0; k < optionsArray.length(); k++) {
                    JSONObject optionItem=(JSONObject)optionsArray.get(k);
                    optionsMapList.add(optionItem.optString("v"));
                }
                specificationMap.put(paramItem.optString("k"),optionsMapList);
            }

        }
    }
}
